package drds.binlog.database_driver.packets.client;

import drds.binlog.common.Authors;
import drds.binlog.database_driver.packets.PacketWithHeaderPacket;
import drds.binlog.database_driver.utils.Bytes;
import drds.binlog.database_driver.utils.MSC;
import drds.binlog.database_driver.utils.PasswordEncrypter;
import drds.common.$;
import drds.common.Author;
import lombok.Getter;
import lombok.Setter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;

@Author(name = Authors.LI_YANG)
public class ClientAuthenticationPacket extends PacketWithHeaderPacket
{

    @Setter
    @Getter
    private String username;
    @Setter
    @Getter
    private String password;
    @Setter
    @Getter
    private byte charsetNumber;
    @Setter
    @Getter
    private String databaseName;
    @Setter
    @Getter
    private int serverCapabilities;
    @Setter
    @Getter
    private byte[] scrumbleBuff;

    public void fromBytes(byte[] bytes)
    {
        // bypass since nowhere to use.
    }

    /**
     * <pre>
     * VERSION 4.1
     *  Bytes                        Name
     *  -----                        ----
     *  4                            client_flags
     *  4                            max_packet_size
     *  1                            charset_number
     *  23                           (filler) always 0x00...
     *  n (Null-Terminated String)   user
     *  n (Length Coded Binary)      scramble_buff (1 + x bytes)
     *  n (Null-Terminated String)   databasename (optional)
     * </pre>
     *
     * @throws IOException
     */
    public byte[] toBytes() throws IOException
    {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 1. write client_flags
        // 1|4|512|1024|8192|32768
        /**
         * CLIENT_LONG_PASSWORD CLIENT_LONG_FLAG CLIENT_PROTOCOL_41
         * CLIENT_INTERACTIVE CLIENT_TRANSACTIONS CLIENT_SECURE_CONNECTION
         * CLIENT_MULTI_STATEMENTS;
         */
        Bytes.writeUnsigned4IntLittleEndian(byteArrayOutputStream, 1 | 4 | 512 | 8192 | 32768 | 0x00010000); // remove
        // client_interactive
        // feature

        // 2. write max_packet_size
        Bytes.writeUnsigned4IntLittleEndian(byteArrayOutputStream, MSC.max_packet_length);
        // 3. write charset_number
        byteArrayOutputStream.write(this.charsetNumber);
        // 4. write (filler) always 0x00...
        byteArrayOutputStream.write(new byte[23]);
        // 5. write (Null-Terminated String) user
        Bytes.writeNullTerminatedString(byteArrayOutputStream, getUsername());
        // 6. write (Length Coded Binary) scramble_buff (1 + x bytes)
        if ($.isNullOrEmpty(getPassword()))
        {
            byteArrayOutputStream.write(0x00);
        } else
        {
            try
            {
                byte[] encryptedPassword = PasswordEncrypter.scramble411(getPassword().getBytes(), scrumbleBuff);
                Bytes.writeLengthAndLengthCodedBytes(byteArrayOutputStream, encryptedPassword);
            } catch (NoSuchAlgorithmException e)
            {
                throw new RuntimeException("can't encrypt password that will be sent to MySQL server.", e);
            }
        }
        // 7 . (Null-Terminated String) databasename (optional)
        if (getDatabaseName() != null)
        {
            Bytes.writeNullTerminatedString(byteArrayOutputStream, getDatabaseName());
        }
        // end write
        return byteArrayOutputStream.toByteArray();
    }


}
